﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using System.Threading;
using Microsoft.Xna.Framework;

namespace Netserver
{
    public class NetworkManager
    {
        NetConfiguration config;
        NetServer server;
        NetBuffer readBuffer;
        Thread heartbeatThread;
        PlayerManager playerManager;

        public NetworkManager()
        {
            // create a configuration for the server
            config = new NetConfiguration("networkdemo");
            config.MaxConnections = 128;
            config.Port = 1000;

            // create server and start listening for connections
            server = new NetServer(config);
            server.SetMessageTypeEnabled(NetMessageType.ConnectionApproval, true);
            server.Start();

            playerManager = new PlayerManager(this);
            readBuffer = server.CreateBuffer();

            heartbeatThread = new Thread(new ThreadStart(Run));
            heartbeatThread.Name = "Message Retrieval thread";
            heartbeatThread.Start();
        }


        private void Run()
        {
            while (true)
            {
                try { Heartbeat(); }
                catch (Exception ex) { Console.WriteLine("Heartbeat failed! {0}", ex.Message); }

                Thread.Sleep(1);
            }
        }

        private void HandleRemPlayer(NetBuffer buff, UInt32 pNum)
        {
            buff.Write("remplayer");
            buff.Write(pNum, 3);
            server.SendToAll(buff, NetChannel.ReliableUnordered);
        }

        private void HandleUpdate(NetBuffer buff, NetConnection sender)
        {
            Player P;
            if ((P = playerManager.UpdatePlayer(buff)) != null)
            {
                buff.Reset();
                buff.Write("updateplayer");
                Player.BufferData(P, buff);
                server.SendToAll(buff, NetChannel.ReliableUnordered, sender);
            }
        }

        private void HandleSendAll(NetBuffer buff, NetConnection sender)
        {
            playerManager.BuffAllPlayers(buff);
            server.SendMessage(buff, sender, NetChannel.ReliableUnordered);
            
        }

        private void HandleNewPlayer(NetBuffer buff, NetConnection sender)
        {
            Player P = playerManager.AddPlayer();
            if (P == null) return;
            P.MyConnection = sender;
            
            buff.Write("id");
            buff.Write((UInt32)P.MyNumber, 3);
            server.SendMessage(buff, sender, NetChannel.ReliableUnordered);
        }

        private void HandleDisconnectingClient(NetConnection sender)
        {
            Player P;
            if ((P = playerManager.GetPlayer(sender)) != null)
            {
                HandleRemPlayer(server.CreateBuffer(), (UInt32)P.MyNumber);
                playerManager.RemPlayer(P); 
            }
        }

        private void Heartbeat()
        {
            // keep running until the user presses a key
            Console.WriteLine("Press ESC to quit server");
            bool keepRunning = true;
            while (keepRunning)
            {
                NetMessageType type;
                NetConnection sender;

                // check if any messages has been received
                while (server.ReadMessage(readBuffer, out type, out sender))
                {
                    switch (type)
                    {
                        case NetMessageType.DebugMessage:
                            Console.WriteLine(readBuffer.ReadString());
                            break;
                        case NetMessageType.ConnectionApproval:
                            Console.WriteLine("Approval; hail is " + readBuffer.ReadString());
                            sender.Approve();
                            break;
                        case NetMessageType.StatusChanged:
                            Console.WriteLine("New status for " + sender + ": " + sender.Status + " (" + readBuffer.ReadString() + ")");
                            switch (sender.Status)
                            {
                                case NetConnectionStatus.Disconnected:
                                case NetConnectionStatus.Disconnecting:
                                    HandleDisconnectingClient(sender);
                                    break;

                                case NetConnectionStatus.Connected:
                                    HandleNewPlayer(server.CreateBuffer(), sender);
                                    
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case NetMessageType.Data:
                            // A client sent this data!
                            string msg = readBuffer.ReadString();
                            switch (msg)
                            {
                                case "update":
                                    HandleUpdate(readBuffer, sender);
                                    break;
                                case "allplayers":
                                    HandleSendAll(server.CreateBuffer(), sender);
                                    break;
                                default:
                                    Console.WriteLine("Unable to understand message type = " + msg);
                                    break;
                            }
                            break;
                    }
                }

                // User pressed ESC?
                while (Console.KeyAvailable)
                {
                    ConsoleKeyInfo info = Console.ReadKey();
                    if (info.Key == ConsoleKey.Escape)
                        keepRunning = false;
                }

                Thread.Sleep(1);
            }

            server.Shutdown("Application exiting");
        }
    }
}

namespace Netserver
{
    class Program
    {
        static void Main(string[] args)
        {
            NetworkManager netman = new NetworkManager();
        }
    }
}
